/**
 * Copyright (c) 2009-2011, chunquedong(YangJiandong)
 * 
 * This file is part of ChunMap project
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE(Version >=3)
 * 
 * History:
 *     2010-05-05  Jed Young  Creation
 */
package chunmap.data.index;

import java.util.ArrayList;
import java.util.List;

import chunmap.model.elem.Envelope;
import chunmap.model.geom.Geometry;

/**
 * 窗口过滤索引
 * 
 * @author chunquedong
 * 
 */
public class Filter3window {
	private Filter3window right;
	private Filter3window middle;
	private Filter3window left;
	private final Envelope window;
	private final List<Geometry> geometrys;
	private final int nowDeep;
	private final int maxDeep;
	private final boolean isVertical;

	public Filter3window(int nowDeep, int maxDeep, Envelope window,
			boolean direction) {
		this.nowDeep = nowDeep;
		this.maxDeep = maxDeep;
		this.window = window;
		this.isVertical = direction;
		geometrys = new ArrayList<Geometry>();
	}

	public void put(Geometry g) {
		if (nowDeep == maxDeep) {
			geometrys.add(g);
		} else {
			if (!putSubWindow(g)) {
				geometrys.add(g);
			}
		}
	}

	private boolean putSubWindow(Geometry g) {
		insureInit();
		if (middle.getEnvelop().contain(g.getEnvelop())) {
			middle.put(g);
			return true;
		} else if (right.getEnvelop().contain(g.getEnvelop())) {
			right.put(g);
			return true;
		} else if (left.getEnvelop().contain(g.getEnvelop())) {
			left.put(g);
			return true;
		}
		return false;
	}

	private void insureInit() {
		boolean b = !isVertical;
		if (middle == null) {
			middle = new Filter3window(nowDeep + 1, maxDeep, mEnv(), b);
			right = new Filter3window(nowDeep + 1, maxDeep, rEnv(), b);
			left = new Filter3window(nowDeep + 1, maxDeep, lEnv(), b);
		}
	}

	private Envelope mEnv() {
		if (isVertical) {
			double oneFour = (window.getMaxX() - window.getMinX()) / 4d;
			double maxX = window.getMaxX() - oneFour;
			double minX = window.getMinX() + oneFour;
			return new Envelope(minX, window.getMinY(), maxX, window.getMaxY());
		} else {
			double oneFour = (window.getMaxY() - window.getMinY()) / 4d;
			double maxY = window.getMaxY() - oneFour;
			double minY = window.getMinY() + oneFour;
			return new Envelope(window.getMinX(), window.getMaxX(), minY, maxY);
		}
	}

	private Envelope rEnv() {
		if (isVertical) {
			double oneTwo = (window.getMaxX() - window.getMinX()) / 2d;
			double maxX = window.getMaxX() - oneTwo;
			double minX = window.getMinX();
			return new Envelope(minX, window.getMinY(), maxX, window.getMaxY());
		} else {
			double oneTwo = (window.getMaxX() - window.getMinX()) / 2d;
			double maxY = window.getMaxY() - oneTwo;
			double minY = window.getMinY();
			return new Envelope(window.getMinX(), window.getMaxX(), minY, maxY);
		}
	}

	private Envelope lEnv() {
		if (isVertical) {
			double oneTwo = (window.getMaxX() - window.getMinX()) / 2d;
			double maxX = window.getMaxX();
			double minX = window.getMinX() + oneTwo;
			return new Envelope(minX, window.getMinY(), maxX, window.getMaxY());
		} else {
			double oneTwo = (window.getMaxX() - window.getMinX()) / 2d;
			double maxY = window.getMaxY();
			double minY = window.getMinY() + oneTwo;
			return new Envelope(window.getMinX(), window.getMaxX(), minY, maxY);
		}
	}

	public Envelope getEnvelop() {
		return window;
	}

	public List<Geometry> search(Envelope env) {
		List<Geometry> geos = new ArrayList<Geometry>();
		geos.addAll(geometrys);
		if (middle != null) {
			if (middle.getEnvelop().hasIntersect(env)) {
				geos.addAll(middle.search(env));
			} else if (right.getEnvelop().hasIntersect(env)) {
				geos.addAll(right.search(env));
			} else if (left.getEnvelop().hasIntersect(env)) {
				geos.addAll(left.search(env));
			}
		}

		return geos;
	}
}